﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DDD;
using Xunit;
using System.IO;

namespace DokiDokiTest {
    public class TestSample {

        /// <summary>
        /// このテストは目視確認用に多数のtをサンプリングしてファイルに出力する
        /// 別途グラフ化して確認する。
        /// </summary>
        [Fact]
        public void Test_All () {
            var kseq = new KeyframeSequence<float> (4, 1);
            kseq.SetKeyframe (0, 1, 100);
            kseq.SetKeyframe (1, 2, 200);
            kseq.SetKeyframe (2, 3, 200);
            kseq.SetKeyframe (3, 4, 300);
            kseq.SetDuration (5);

            kseq.SetInterpolationMode (InterpolationType.Step);
            kseq.SetRepeatMode (KeyframeRepeatMode.Once);
            Print ("Sample-Step-Once.dat", -10, 10, 200, kseq);

            kseq.SetInterpolationMode (InterpolationType.Step);
            kseq.SetRepeatMode (KeyframeRepeatMode.Loop);
            Print ("Sample-Step-Loop.dat", -10, 10, 200, kseq);

            kseq.SetInterpolationMode (InterpolationType.Linear);
            kseq.SetRepeatMode (KeyframeRepeatMode.Once);
            Print ("Sample-Linear-Once.dat", -10, 10, 200, kseq);

            kseq.SetInterpolationMode (InterpolationType.Linear);
            kseq.SetRepeatMode (KeyframeRepeatMode.Loop);
            Print ("Sample-Linear-Loop.dat", -10, 10, 200, kseq);

            kseq.SetInterpolationMode (InterpolationType.Spline);
            kseq.SetRepeatMode (KeyframeRepeatMode.Once);
            Print ("Sample-Spline-Once.dat", -10, 10, 200, kseq);

            kseq.SetInterpolationMode (InterpolationType.Spline);
            kseq.SetRepeatMode (KeyframeRepeatMode.Loop);
            Print ("Sample-Spline-Loop.dat", -10, 10, 200, kseq);


        }

        void Print (string name, float start, float end, int times, KeyframeSequence kseq) {
            using (StreamWriter w = new StreamWriter (@name)) {
                var value = new float[kseq.ComponentCount];
                for (var i = 0; i < times; i++) {
                    var time = start + i * (end - start) / (float)times;
                    kseq.Sample (time, value);
                    w.WriteLine (time + "   " + value[0]);
                }
            }
        }

        [Fact]
        public void Test_Step_Once () {
            var kseq = new KeyframeSequence<float> (4, 1);
            kseq.SetKeyframe (0, 1, 100);
            kseq.SetKeyframe (1, 2, 200);
            kseq.SetKeyframe (2, 3, 200);
            kseq.SetKeyframe (3, 4, 300);
            kseq.SetDuration (5);
            kseq.SetInterpolationMode (InterpolationType.Step);
            kseq.SetRepeatMode (KeyframeRepeatMode.Once);

            var value = new float[1];

            // キーフレームの中
            kseq.Sample (2.5f, value);
            Assert.Equal (200, value[0]);

            // キーフレームの外、Durationの中
            kseq.Sample (0.5f, value);
            Assert.Equal (100, value[0]);

            kseq.Sample (4.5f, value);
            Assert.Equal (300, value[0]);

            // Durationの外
            kseq.Sample (-0.5f, value);
            Assert.Equal (100, value[0]);

            kseq.Sample (5.5f, value);
            Assert.Equal (300, value[0]);
        }

        [Fact]
        public void Test_Step_Loop () {
            var kseq = new KeyframeSequence<float> (4, 1);
            kseq.SetKeyframe (0, 1, 100);
            kseq.SetKeyframe (1, 2, 200);
            kseq.SetKeyframe (2, 3, 200);
            kseq.SetKeyframe (3, 4, 300);
            kseq.SetDuration (5);
            kseq.SetInterpolationMode (InterpolationType.Step);
            kseq.SetRepeatMode (KeyframeRepeatMode.Loop);

            var value = new float[1];

            // キーフレームの中
            kseq.Sample (2.5f, value);
            Assert.Equal (200, value[0]);

            // キーフレームの外、Durationの中
            kseq.Sample (0.5f, value);
            Assert.Equal (300, value[0]);

            kseq.Sample (4.5f, value);
            Assert.Equal (300, value[0]);

            // Durationの外
            kseq.Sample (-0.5f, value);
            Assert.Equal (300, value[0]);

            kseq.Sample (5.5f, value);
            Assert.Equal (300, value[0]);
        }

        [Fact]
        public void Test_Linear_Once () {
            var kseq = new KeyframeSequence<float> (4, 1);
            kseq.SetKeyframe (0, 1, 100);
            kseq.SetKeyframe (1, 2, 200);
            kseq.SetKeyframe (2, 3, 200);
            kseq.SetKeyframe (3, 4, 300);
            kseq.SetDuration (5);
            kseq.SetInterpolationMode (InterpolationType.Linear);
            kseq.SetRepeatMode (KeyframeRepeatMode.Once);

            var value = new float[1];

            // キーフレームの中
            kseq.Sample (2.5f, value);
            Assert.Equal (200, value[0], new FloatComparer (0.0001f));

            // キーフレームの外、Durationの中
            kseq.Sample (0.5f, value);
            Assert.Equal (100, value[0], new FloatComparer (0.0001f));

            kseq.Sample (4.5f, value);
            Assert.Equal (300, value[0], new FloatComparer (0.0001f));

            // Durationの外
            kseq.Sample (-0.5f, value);
            Assert.Equal (100, value[0], new FloatComparer (0.0001f));

            kseq.Sample (5.5f, value);
            Assert.Equal (300, value[0], new FloatComparer (0.0001f));
        }

        [Fact]
        public void Test_Linear_Loop () {
            var kseq = new KeyframeSequence<float> (4, 1);
            kseq.SetKeyframe (0, 1, 100);
            kseq.SetKeyframe (1, 2, 200);
            kseq.SetKeyframe (2, 3, 200);
            kseq.SetKeyframe (3, 4, 300);
            kseq.SetDuration (5);
            kseq.SetInterpolationMode (InterpolationType.Linear);
            kseq.SetRepeatMode (KeyframeRepeatMode.Loop);

            var value = new float[1];

            // キーフレームの中
            kseq.Sample (2.5f, value);
            Assert.Equal (200, value[0], new FloatComparer(0.0001f));

            // キーフレームの外、Durationの中
            kseq.Sample (0.5f, value);
            Assert.Equal (150, value[0], new FloatComparer (0.0001f));

            kseq.Sample (4.5f, value);
            Assert.Equal (250, value[0], new FloatComparer (0.0001f));

            // Durationの外
            kseq.Sample (-0.5f, value);
            Assert.Equal (250, value[0], new FloatComparer (0.0001f));

            kseq.Sample (5.5f, value);
            Assert.Equal (150, value[0], new FloatComparer (0.0001f));
        }

        [Fact]
        public void Test_Slerp_Once () {
            var kseq = new KeyframeSequence<float> (4, 4);
            var q1 = new Quaternion (0, 0, 0, 1);
            var q2 = new Quaternion (90, 0, 0, 1);
            var q3 = new Quaternion (180, 0, 0, 1);
            var q4 = new Quaternion (270, 0, 0, 1);
            kseq.SetKeyframe (0, 0, q1[0], q1[1], q1[2], q1[3]);
            kseq.SetKeyframe (1, 1, q2[0], q2[1], q2[2], q2[3]);
            kseq.SetKeyframe (2, 2, q3[0], q3[1], q3[2], q3[3]);
            kseq.SetKeyframe (3, 3, q4[0], q4[1], q4[2], q4[3]);
            kseq.SetDuration (4);
            kseq.SetInterpolationMode (InterpolationType.Slerp);
            kseq.SetRepeatMode (KeyframeRepeatMode.Once);

            var value = new float[4];

            kseq.Sample (0, value);
            Assert.Equal (0, Quaternion.Set(value).Angle, new FloatComparer (0.1f));

            kseq.Sample (0.5f, value);
            Assert.Equal (45.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));

            kseq.Sample (1.0f, value);
            Assert.Equal (90.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));

            // Durationの外
            kseq.Sample (-0.5f, value);
            Assert.Equal (0.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));

            // Durationの外
            kseq.Sample (4.5f, value);
            Assert.Equal (270.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));
        }

        [Fact]
        public void Test_Slerp_Loop () {
            var kseq = new KeyframeSequence<float> (4, 4);
            var q1 = new Quaternion (0, 0, 0, 1);
            var q2 = new Quaternion (90, 0, 0, 1);
            var q3 = new Quaternion (180, 0, 0, 1);
            var q4 = new Quaternion (270, 0, 0, 1);
            kseq.SetKeyframe (0, 0, q1[0], q1[1], q1[2], q1[3]);
            kseq.SetKeyframe (1, 1, q2[0], q2[1], q2[2], q2[3]);
            kseq.SetKeyframe (2, 2, q3[0], q3[1], q3[2], q3[3]);
            kseq.SetKeyframe (3, 3, q4[0], q4[1], q4[2], q4[3]);
            kseq.SetDuration (4);
            kseq.SetInterpolationMode (InterpolationType.Slerp);
            kseq.SetRepeatMode (KeyframeRepeatMode.Loop);

            var value = new float[4];

            kseq.Sample (0, value);
            Assert.Equal (0, Quaternion.Set (value).Angle, new FloatComparer (0.1f));

            kseq.Sample (0.5f, value);
            Assert.Equal (45.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));

            kseq.Sample (1.0f, value);
            Assert.Equal (90.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));

            // Durationの外
            kseq.Sample (-0.5f, value);
            Assert.Equal (135.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));

            // Durationの外
            kseq.Sample (4.5f, value);
            Assert.Equal (45.0f, Quaternion.Set (value).Angle, new FloatComparer (0.1f));
        }
    }
}
